package chabernac.android.wordbattle.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import android.util.Log;

import chabernac.android.wordbattle.db.iStringDatabase;
import chabernac.android.wordbattle.rastersolver.WordRasterSolver;

public class WordRaster extends Game{
  private final Letter[][] myRaster;

  private final List<Letter> mySelectedLetters = new ArrayList<Letter>();
  private final Map<Word, Player> myFoundWords = new LinkedHashMap<Word, Player>();

  private final Set<iWordRasterListener> myListeners = new HashSet<iWordRasterListener>();

  private final iSelectionHelper mySelectionHelper;

  private final iScoreCalculator myScoreCalculator;

  private final String myLanguage;
  
  private Set<String> myWordsInRaster = new HashSet<String>();
  private final iStringDatabase myStringDatabase;

  public WordRaster(Player aLocalPlayer, Letter[][] aRaster, iStringDatabase aStringDatabase, iSelectionHelper aSelectionHelper, String aLanguage, iScoreCalculator aScoreCalculator){
    super(aLocalPlayer);
    myRaster = aRaster;
    mySelectionHelper = aSelectionHelper;
    myLanguage = aLanguage;
    myScoreCalculator = aScoreCalculator;
    myStringDatabase = aStringDatabase;
  }

  public WordRaster(Player aLocalPlayer, int aColumns, int aRows, iStringDatabase aWordValidator, iSelectionHelper aSelectionHelper, String aLanguage, iScoreCalculator aScoreCalculator){
    this(aLocalPlayer, new Letter[aColumns][aRows], aWordValidator, aSelectionHelper, aLanguage, aScoreCalculator);
  }
  
  public void solve(){
    long t1 = System.currentTimeMillis();
    myWordsInRaster.addAll( new WordRasterSolver( myStringDatabase ).solve( this, 3 ));
    long t2 = System.currentTimeMillis();
    Log.d( WordRaster.class.getName(), "Solving wordraster took '"+ (t2 - t1) + "' ms" );
    for(String theWord : myWordsInRaster) Log.d(WordRaster.class.getName(), theWord);
  }

  public int getColumns(){
    return myRaster.length;
  }

  public int getRows(){
    return myRaster[0].length;
  }

  public void setLetter(int aColumn, int aRow, char aLetter){
    myRaster[aColumn][aRow] = new Letter(aLetter, aColumn, aRow);
  }

  public Letter getLetter(int aColumn, int aRow){
    return myRaster[aColumn][aRow];
  }

  public void setSelectedLetter(int aColumn, int arow, boolean isSelected){
    if(aColumn < 0) return;
    if(aColumn >= getColumns()) return;
    if(arow < 0) return;
    if(arow >= getRows()) return;


    //    if(!mySelectedLetters.isEmpty()){
    //      Letter theFirstLetter = mySelectedLetters.get(0);
    //      if(aColumn == theFirstLetter.getColumn()){
    //        mySelectedLetters.clear();
    //        for(int row = theFirstLetter.getRow(); row<=arow; row++){ 
    //          mySelectedLetters.add(myRaster[aColumn][row]);
    //        }
    //      }
    //      
    //      if(arow == theFirstLetter.getRow()){
    //        mySelectedLetters.clear();
    //        for(int column = theFirstLetter.getColumn(); column<=arow; column++){ 
    //          mySelectedLetters.add(myRaster[column][arow]);
    //        }
    //      }
    //    }

    //we must be able to deselecte letters by moving your finger back over the selection
    if(mySelectedLetters.size() >= 2){
      Letter theLetter = mySelectedLetters.get(mySelectedLetters.size() - 2);
      if(theLetter.getColumn() == aColumn && theLetter.getRow() == arow){
        //deselect the last letter
        mySelectedLetters.remove(mySelectedLetters.size() -1 ).setSelected( false );

        notifyListeners();
        return;
      }
    }

    //add an extra selected letter
    if(!mySelectedLetters.contains(myRaster[aColumn][arow])){
      //first check if the selection is allowed
      if(mySelectionHelper.isSelectionAllowed(this, myRaster[aColumn][arow])){
        System.out.println("Letter selected '" + myRaster[aColumn][arow].getChar() + "'");
        myRaster[aColumn][arow].setSelected(isSelected);
        //only add the new letter if it was not yet selected
        mySelectedLetters.add(myRaster[aColumn][arow]);
        notifyListeners();
      }
    }
  }

  public void clearSelectedLetters(){
    for(Letter theLetter : mySelectedLetters) theLetter.setSelected(false);
        mySelectedLetters.clear();
        notifyListeners();
  }

  public String getSelectedWord(){
    String theSelectedWord = "";
    for(Letter thetLetter : mySelectedLetters){
      theSelectedWord += thetLetter.getChar();
    }
    return theSelectedWord;
  }

  public iSelectionHelper getSelectionHelper() {
    return mySelectionHelper;
  }

  public void checkForHiddenWord(){
    final String theSelectedWord = getSelectedWord();
    final Word theWord = new Word(new ArrayList<Letter>(mySelectedLetters));
    if(!myFoundWords.containsKey( theWord)){
      if(myWordsInRaster.contains( theSelectedWord)){
        myFoundWords.put(theWord, getLocalPlayer());
        notifyWordFound(theWord);
      }
    } else {
      //we already found this word so we know it is a valid word
      //for some game types it is necessary that this word is set as last word
      //so remove the word and add it again, it will now be the last found word
      myFoundWords.remove( theWord );
      myFoundWords.put( theWord, getLocalPlayer() );
      //nofity the listeners
      notifyWordFound( theWord );
    }
    clearSelectedLetters();
  }

  public List<Letter> getSelectedLetters(){
    return Collections.unmodifiableList(mySelectedLetters);
  }

  public Map<Word, Player> getFoundWords(){
    synchronized(myFoundWords){
      return Collections.unmodifiableMap(new LinkedHashMap<Word, Player>(myFoundWords));
    }
  }

  public void addListener(iWordRasterListener aListener){
    myListeners.add(aListener);
  }

  public void removeListener(iWordRasterListener aListener){
    myListeners.remove(aListener);
  }

  public void notifyListeners(){
    for(iWordRasterListener theListener : myListeners){
      theListener.selectionChanged(mySelectedLetters);
    }
  }

  public void notifyWordFound(Word aWord){
    for(iWordRasterListener theListener : myListeners){
      theListener.wordFound(aWord);
    }
  }

  public List<Word> getFoundWordsByLocalPlayer(){
    return getFoundWordsByPlayer( getLocalPlayer() );
  }

  public List<Word> getFoundWordsByPlayer(Player aPlayer){
    List<Word> theWords = new ArrayList<Word>();

    for ( Word theWord : myFoundWords.keySet() ) {
      Player thePlayer = myFoundWords.get( theWord );
      if(thePlayer == aPlayer){
        theWords.add(theWord);
      }
    }

    return theWords;
  }

  public Word getLastFoundWord(){
    List<Word> theWordsFoundByLocalPlayer = getFoundWordsByLocalPlayer();
    if(theWordsFoundByLocalPlayer.isEmpty()) return null;
    return theWordsFoundByLocalPlayer.get( theWordsFoundByLocalPlayer.size() - 1 );
  }

  public int getScore(Player aPlayer){
    return myScoreCalculator.calculateScore( getFoundWordsByPlayer( aPlayer ) );
  }

  public Letter[][] getRaster() {
    return myRaster;
  }

  public String getLanguage() {
    return myLanguage;
  }
  
  public Set<String> getWordsInRaster() {
    return myWordsInRaster;
  }
  
  public void setWordsInRaser(Set<String> aWords){
  	myWordsInRaster = aWords;
  }

  public List<Letter> getNeighbours(Letter aStartingLetter) {
    List<Letter> theLetters = new ArrayList<Letter>();
    for(int row = aStartingLetter.getRow() -1;row<=aStartingLetter.getRow()+1;row++){
      for(int column = aStartingLetter.getColumn()-1;column<=aStartingLetter.getColumn()+1;column++){
        if(row >=0 && row < getRows() && column>=0 && column <getColumns() && !(row == aStartingLetter.getRow() && column == aStartingLetter.getColumn())){
          theLetters.add(getLetter(column, row));
        }
      }
    }
    return theLetters;
  }
  
	public String toString(){
		String theRaster = "";
		for(int column=0;column<myRaster.length;column++){
			for(int row=0;row<myRaster[column].length;row++){
				theRaster += myRaster[column][row].getChar();
			}
			theRaster += "\r\n";
		}
		return theRaster;
	}
	
}
